package agu.json;

import org.json.JSONArray;
import org.json.JSONObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

@SuppressWarnings("unchecked")
public class JSONUtil
{
    public static JSONObject2 newJSONObject(Object input){
        return new JSONObject2(input);
    }

    public static JSONArray2 newJSONArray(Object input){
        return new JSONArray2(input);
    }

    public interface EachCallback<Obj, Key>
    {
        void onEach(Obj obj, Key key);
    }

    public static <JSONArray2 extends JSONArray> void forEach(JSONArray2 array,
        EachCallback<JSONArray2, Integer> callback){
        for(int key = 0; ; key++){
            int length = array.length();
            if(key >= length) break;

            callback.onEach(array, key);

            int remove = array.length() - length;
            if(remove > 0) key -= remove;
        }
    }

    public static <JSONObject2 extends JSONObject> void forEach(JSONObject2 obj,
        EachCallback<JSONObject2, String> callback){
        for(Iterator<String> it = obj.keys(); it.hasNext(); ){
            String key = it.next();
            callback.onEach(obj, key);
        }
    }

    static <F> F getField(Object obj, Class<?> ownerType, Class<F> fieldType){
        try{
            for(Field field: ownerType.getDeclaredFields()){
                if(fieldType.isAssignableFrom(field.getType())){
                    field.setAccessible(true);
                    Object value = field.get(obj);
                    return fieldType.cast(value);
                }
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }

        return null;
    }

    public static List<Object> toList(Object obj){
        if(obj == null) return Collections.emptyList();

        if(obj instanceof List){
            return (List<Object>)obj;
        }

        if(obj instanceof Iterable){
            obj = ((Iterable<?>)obj).iterator();
        }

        if(obj instanceof Iterator){
            List<Object> res = new ArrayList<>();
            for(Iterator<?> it = (Iterator<?>)obj; it.hasNext(); ){
                res.add(it.next());
            }
            return res;
        }

        if(obj instanceof JSONArray){
            return JSONUtil.getField(obj, JSONArray.class, List.class);
        }

        List<Object> res = new ArrayList<>();
        try{
            final int length = Array.getLength(obj);
            for(int i = 0; i < length; ++i){
                res.add(Array.get(obj, i));
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }
        return res;
    }

    public static Map<String, Object> toMap(Object obj){
        if(obj == null) return Collections.emptyMap();

        if(obj instanceof Map){
            return (Map<String, Object>)obj;
        }

        if(obj instanceof JSONObject){
            return getField(obj, JSONObject.class, HashMap.class);
        }

        LinkedHashMap<String, Object> res = new LinkedHashMap<>();
        try{
            for(Field field: obj.getClass().getFields()){
                if(Modifier.isStatic(field.getModifiers())) continue;
                if(Modifier.isTransient(field.getModifiers())) continue;
                String name = field.getName();
                Object value = field.get(obj);
                res.put(name, value);
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }
        return res;
    }
}
